/*
 * Copyright (c) 2020, HiHope Community.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "ht30.h"

#include <stdio.h>
#include <unistd.h>
#include <string.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifiiot_gpio.h"
#include "wifiiot_gpio_ex.h"
#include "wifiiot_i2c.h"
#include "wifiiot_gpio_w800.h"

#include "oled_ssd1306.h"

#include "net_params.h"
#include "wifi_connecter.h"
#include "net_common.h"


#define LED_TASK_STACK_SIZE 512
#define LED_TASK_PRIO 25

enum LedState {
    LED_ON = 0,
    LED_OFF,
    LED_SPARK,
};

enum LedState g_ledState = LED_SPARK;

static void* GpioTask(const char* arg)
{
    (void)arg;
    while (1) {
        switch (g_ledState) {
            case LED_ON:
                printf(" LED_ON! \n");
                GpioSetOutputVal(WIFI_IOT_GPIO_PB_00, WIFI_IOT_GPIO_VALUE0);
                osDelay(500);
                break;
            case LED_OFF:
                printf(" LED_OFF! \n");
                GpioSetOutputVal(WIFI_IOT_GPIO_PB_00, WIFI_IOT_GPIO_VALUE1);
                osDelay(500);
                break;
            case LED_SPARK:
                printf(" LED_SPARK! \n");
                GpioSetOutputVal(WIFI_IOT_GPIO_PB_00, WIFI_IOT_GPIO_VALUE0);
                osDelay(500);
                printf(" LED_SPARK!2 \n");
                GpioSetOutputVal(WIFI_IOT_GPIO_PB_00, WIFI_IOT_GPIO_VALUE1);
                osDelay(500);
                break;
            default:
                osDelay(500);
                break;
        }
    }

    return NULL;
}

static void GpioIsr(char* arg)
{
    (void)arg;

    enum LedState nextState = LED_SPARK;

    printf(" GpioIsr entry\n");

    GpioSetIsrMask(WIFI_IOT_GPIO_PB_07, 0);
    switch (g_ledState) {
        case LED_ON:
            nextState = LED_OFF;
            break;
        case LED_OFF:
            nextState = LED_ON;
            break;
        case LED_SPARK:
            nextState = LED_OFF;
            break;
        default:
            break;
    }

    g_ledState = nextState;
}

void HT30TestTask(void* arg)
{
    
    (void) arg;

    int times = 0;

    uint32_t retval = 0;


    WifiDeviceConfig config = {0};

    // 准备AP的配置参数， 连接WiFi
    strcpy(config.ssid, PARAM_HOTSPOT_SSID);
    strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
    config.securityType = PARAM_HOTSPOT_TYPE;

    osDelay(10);

    int netId = ConnectToHotspot(&config);

    // 建立UDP连接，这里充当了UDP的客户端

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // UDP socket
    struct sockaddr_in toAddr = {0};
    toAddr.sin_family = AF_INET;
    toAddr.sin_port = htons(PARAM_SERVER_PORT); // 端口号，从主机字节序转为网络字节序
    if (inet_pton(AF_INET, PARAM_SERVER_ADDR, &toAddr.sin_addr) <= 0) { // 将主机IP地址从“点分十进制”字符串 转化为 标准格式（32位整数）
        printf("inet_pton failed!\r\n");
        goto do_cleanup;
    }

    // I2C和OLED的初始化。

    if (I2cInit(WIFI_IOT_I2C_IDX_0, 200*1000)) {
        printf("HT30 test i2c init failed\n");
    }

    OledInit();

    OledFillScreen(0x00);
    OledShowString(0, 0, "** HarmonyOS! **", 1);
    osDelay(400);


    
    OledShowString(0, 1, "** HarmonyOS! **", 1);
    OledShowString(0, 2, "****************", 1);
    OledShowString(0, 3, "****************", 1);

    // 每秒测量一次温湿度数据
    while (1) {

        retval = HT30_StartMeasure();
        printf("HT30_StartMeasure: %d\r\n", retval);

        float temp = 0.0, humi = 0.0;

        retval = HT30_GetMeasureResult(&temp, &humi);
        printf("HT30_GetMeasureResult: %d, temp = %.2f, humi = %.2f\r\n", retval, temp, humi);

        times++;

        // 将温湿度数据显示在OELD屏幕上
        static char line1[32] = {0};
        snprintf(line1, sizeof(line1), "** times = [%d]", times);
        OledShowString(0, 1, line1, 1);

        static char line2[32] = {0};
        snprintf(line2, sizeof(line2), "** temp : %.2f", temp);
        OledShowString(0, 2, line2, 1);

        static char line3[32] = {0};
        snprintf(line3, sizeof(line3), "** humi : %d", (int)humi);
        OledShowString(0, 3, line3, 1);

        // 将温湿度数据作为UDP的消息发送给手机
        static char udpmessage[7] = {0};
        snprintf(udpmessage, sizeof(udpmessage), "%04d%02d", (int)(temp*100), (int)humi);

        // UDP socket 是 “无连接的” ，因此每次发送都必须先指定目标主机和端口，主机可以是多播地址
        retval = sendto(sockfd, udpmessage, sizeof(udpmessage), 0, (struct sockaddr *)&toAddr, sizeof(toAddr));
        if (retval < 0) {
            printf("sendto failed!\r\n");
            goto do_cleanup;
        }
        printf("send UDP message {%s} %ld done!\r\n", udpmessage, retval);
        // 延时1秒
        osDelay(500);
    }

do_cleanup:
    printf("do_cleanup...\r\n");
    close(sockfd);
}


void HT30Test(void)
{
    GpioInit();
    GpioSetDir(WIFI_IOT_GPIO_PB_00, WIFI_IOT_GPIO_DIR_OUTPUT); // output is 0 PB08 control led

    GpioSetDir(WIFI_IOT_GPIO_PB_07, WIFI_IOT_GPIO_DIR_INPUT); // input is PB09
    IoSetPull(WIFI_IOT_GPIO_PB_07, WIFI_IOT_GPIO_ATTR_PULLHIGH);
    GpioRegisterIsrFunc(WIFI_IOT_GPIO_PB_07, WIFI_IOT_INT_TYPE_EDGE, WIFI_IOT_GPIO_EDGE_FALL_LEVEL_LOW, GpioIsr, NULL);

    // 温湿度测量线程

    osThreadAttr_t attr;

    attr.name = "HT30Task";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 4096;
    attr.priority = osPriorityNormal;

    if (osThreadNew(HT30TestTask, NULL, &attr) == NULL) {
        printf("[HT30Test] Failed to create HT30TestTask!\n");
    }

    // OLED闪烁线程
    osThreadAttr_t attr2;

    attr2.name = "HT30Task2";
    attr2.attr_bits = 0U;
    attr2.cb_mem = NULL;
    attr2.cb_size = 0U;
    attr2.stack_mem = NULL;
    attr2.stack_size = 4096;
    attr2.priority = osPriorityNormal;

    if (osThreadNew(GpioTask, NULL, &attr2) == NULL) {
        printf("[HT30Test] Failed to create HT30TestTask2!\n");
    }
}

APP_FEATURE_INIT(HT30Test);